package com.wdy.golden.tinysql.core;

import java.io.File;
import java.lang.reflect.Method;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.simple.SimpleJdbcCall;
import org.springframework.jdbc.object.BatchSqlUpdate;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wdy.golden.tinysql.annotation.TinyBatch;
import com.wdy.golden.tinysql.annotation.TinyCall;
import com.wdy.golden.tinysql.annotation.TinyDelete;
import com.wdy.golden.tinysql.annotation.TinyFunc;
import com.wdy.golden.tinysql.annotation.TinyInsert;
import com.wdy.golden.tinysql.annotation.TinyInterface;
import com.wdy.golden.tinysql.annotation.TinyMethod;
import com.wdy.golden.tinysql.annotation.TinySecondCache;
import com.wdy.golden.tinysql.annotation.TinySelect;
import com.wdy.golden.tinysql.annotation.TinyUpdate;
import com.wdy.golden.tinysql.constant.TinysqlConstants;
import com.wdy.golden.tinysql.enmus.TinysqlSchema;
import com.wdy.golden.tinysql.exception.TinySqlException.*;
import com.wdy.golden.tinysql.extend.ResultColumnArrayList;
import com.wdy.golden.tinysql.extend.SqlTypeConverter;
import com.wdy.golden.tinysql.extend.SqlTypeFactory;
import com.wdy.golden.tinysql.mapping.ResultMap;
import com.wdy.golden.tinysql.mapping.Script;
import com.wdy.golden.tinysql.mapping.SqlScript;
import com.wdy.golden.tinysql.mapping.Tinysql;
import com.wdy.golden.tinysql.plus.builder.ITinySqlBuilder;
import com.wdy.golden.tinysql.plus.common.ITinysqlPlus;
import com.wdy.golden.tinysql.plus.util.PlusArgumentUtil;
import com.wdy.golden.tinysql.plus.util.PlusClassUtil;
import com.wdy.golden.tinysql.plus.util.PlusSqlScriptUtil;
import com.wdy.golden.tinysql.util.ClazzUtil;
import com.wdy.golden.tinysql.util.StringUtil;
import com.wdy.golden.tinysql.util.ArgumentUtil;
import com.wdy.golden.tinysql.util.MappingUtil;
import com.wdy.golden.tinysql.util.ReflectionUtil;
import com.wdy.golden.tinysql.util.ResultSetUtil;
import com.wdy.golden.tinysql.util.TinysqlSpringUtil;

/**
 * @author Dongye_Wu
 * @version: 1.0.0
 * @Description: TODO
 * @date 2020年4月9日 下午8:33:05
 */
@Component
public class TinysqlKeeper implements ITinysql, InitializingBean {
	private static final Logger log = LoggerFactory.getLogger(TinysqlKeeper.class);
	
	private ConcurrentHashMap<String, Object> sqlMap = new ConcurrentHashMap<String, Object>();

	//tinysql config
	@Value("${tinysql.mappingFilePath}")
	private String mappingFilePath;
	@Value("${tinysql.mappingInterfacePackage}")
	private String mappingInterfacePackage;
	@Value("${tinysql.namedParameterJdbcTemplateBeanName:namedParameterJdbcTemplate}")
	private String namedParameterJdbcTemplateBeanName;
	//second cache config
	@Value("${tinysql.secondCache.enable:false}")
	private boolean secondCacheEnable;
	@Value("${tinysql.secondCache.redisTemplateBeanName:stringRedisTemplate}")
	private String redisTemplateBeanName;
	@Value("${tinysql.secondCache.globalExprie:3600}")
	private int secondCacheGlobalExprie;
	@Value("${tinysql.secondCache.redisPath:'tinycache:default:'}")
	private String secondCacheRedisPath;
	//dependence bean
	private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
	private StringRedisTemplate redisTemplate;
	
	private void validateConfig() {
		//tinysql config validate
		if (StringUtil.isEmpty(mappingFilePath)) {
			Assert.notNull(mappingFilePath, "[Tinysql] config mappingFilePath must not be null");
		}
		if (StringUtil.isEmpty(mappingInterfacePackage)) {
			Assert.notNull(mappingInterfacePackage, "[Tinysql] config mappingInterfacePackage must not be null");
		}
	}

	private void initJdbcTemplate() {
		if (namedParameterJdbcTemplate == null) {
			namedParameterJdbcTemplate = TinysqlSpringUtil.getBean(namedParameterJdbcTemplateBeanName, NamedParameterJdbcTemplate.class);
			Assert.notNull(namedParameterJdbcTemplate, "[Tinysql] config jdbcTemplateBeanName must not be null");
		}
	}

	private void initRedisTemplate() {
		if (redisTemplate == null) {
			redisTemplate = TinysqlSpringUtil.getBean(redisTemplateBeanName, StringRedisTemplate.class);
			Assert.notNull(redisTemplate, "[Tinysql] config redisTemplateBeanName must not be null");
		}
	}

	private void processMappingFiles() throws FileException {
		List<File> files = MappingUtil.getMappingFiles(mappingFilePath);
		if (files != null && files.size() > 0) {
			for (File file : files) {
				log.debug("[Tinysql] find mapping file : {}", file.getName());
				parseMappingFile(file);
			}
		}
	}

	private void parseMappingFile(File file) {
		log.debug("[Tinysql] start to parse mapping file {}", file.getName());
		Tinysql tinysql = MappingUtil.readMappingFile(file);
		if(tinysql!=null) {
			String infName = tinysql.getInf().getName();
			
			List<Script> scripts = tinysql.getScripts();
			if (scripts != null && scripts.size() > 0) {
				for (Script script : scripts) {
					log.debug("[Tinysql] mapping file {}, script {}", file.getName(), script);
					sqlMap.put(
						MappingUtil.getMapKey(infName, TinysqlSchema.SCRIPT.getName(), script.getMethod()),
						script
					);
				}
			}
			List<ResultMap> resultMaps = tinysql.getResultMaps();
			if (resultMaps != null && resultMaps.size() > 0) {
				for (ResultMap resultMap : resultMaps) {
					log.debug("[Tinysql] mapping file {}, resultMap {}", file.getName(), resultMap);
					sqlMap.put(
						MappingUtil.getMapKey(infName, TinysqlSchema.RESULTMAP.getName(), resultMap.getName()),
						resultMap
					);
				}
			}
		}		
		//删除mapping文件
		if(file.exists()) {
			file.delete();
		}
	}
	
	private Map<String, Object> getArgMap(Class<?> interfaceType, Method method, Object[] args){
		if(
				method.getDeclaringClass() == ITinysqlPlus.class
				||
				method.getDeclaringClass() == ITinySqlBuilder.class
		) {
			return PlusArgumentUtil.processArguments(interfaceType, method, args);
		}else {
			return ArgumentUtil.processArguments(method, args);
		}
	}
	
	private SqlScript getSqlScript(Class<?> interfaceType, Method method, Object[] args, Script script, Map<String, Object> argMap) {
		if(
				method.getDeclaringClass() == ITinysqlPlus.class
				||
				method.getDeclaringClass() == ITinySqlBuilder.class
		) {
			return PlusSqlScriptUtil.getSqlScript(interfaceType, method, args, argMap);
		}else {
			return MappingUtil.getSqlScript(script, argMap);
		}
	}
	
	private Class<?> getReturnTypeClass(Class<?> interfaceType, Method method) {
		if(
				method.getDeclaringClass() == ITinysqlPlus.class
				||
				method.getDeclaringClass() == ITinySqlBuilder.class
		) {
			Class<?> returnType = method.getReturnType();
			if(returnType==Object.class) {
				return PlusClassUtil.getInterfaceGenericClass(interfaceType, ITinysqlPlus.class);
			}
		}
		return method.getReturnType();
	}
	
	private Class<?> getListGenericReturnType(Class<?> interfaceType, Method method){
		//get return list's GenericType
		Class<?> genericType = null;
		if(method.getDeclaringClass() == ITinysqlPlus.class) {
			genericType = PlusClassUtil.getInterfaceGenericClass(interfaceType, ITinysqlPlus.class);
		}else if (method.getDeclaringClass() == ITinySqlBuilder.class){
			genericType = PlusClassUtil.getInterfaceGenericClass(interfaceType, ITinySqlBuilder.class);
		}else {
			try {
				genericType = ClazzUtil.getListGenericReturnType( method);
			} catch (SqlParseException e) {
				e.printStackTrace();
			}
		}
		return genericType;
	}
	
	private SqlScript sqlPlaceholderHandler(SqlScript sqlScript, Map<String, Object> argMap) {
		String originSql = sqlScript.getOriginSql();
		String paginationSql = sqlScript.getPaginationSql();
		String countSql = sqlScript.getCountSql();
		
		//map loop
		if(argMap!=null && argMap.size()>0) {
			for (Map.Entry<String, Object> argMapEntry : argMap.entrySet()) {
				if(StringUtil.isNotBlank(argMapEntry.getKey())) {
					String placeHolderStr = TinysqlConstants.WELLNUMBER + argMapEntry.getKey();
					if(
							StringUtil.isNotEmpty(originSql) 
							&& StringUtil.contains(originSql, placeHolderStr) 
					) {
						originSql = StringUtil.replace(originSql, placeHolderStr, StringUtil.defaultIfBlank((String)argMapEntry.getValue(), ""));
					}
					
					if(
							StringUtil.isNotEmpty(paginationSql)
							&& StringUtil.contains(paginationSql, placeHolderStr) 
					) {
						paginationSql = StringUtil.replace(paginationSql, placeHolderStr, StringUtil.defaultIfBlank((String)argMapEntry.getValue(), ""));
					}
					
					if(
							StringUtil.isNotEmpty(countSql)
							&& StringUtil.contains(countSql, placeHolderStr) 
					) {
						countSql = StringUtil.replace(countSql, placeHolderStr, StringUtil.defaultIfBlank((String)argMapEntry.getValue(), ""));
					}
				}
			}
		}
		
		sqlScript.setOriginSql(originSql);
		sqlScript.setPaginationSql(paginationSql);
		sqlScript.setCountSql(countSql);
		
		return sqlScript;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Object executeSql(Class<?> interfaceType, Method method, Object[] args) throws SqlParseException, ImperfectException {
		String interfaceName = null;
		Class<?> clazz = method.getDeclaringClass();
		if (clazz.isAnnotationPresent(TinyInterface.class)) {
			TinyInterface tinyInterface = clazz.getAnnotation(TinyInterface.class);
			interfaceName = tinyInterface.name();
		}
		if (interfaceName == null) {
			interfaceName = clazz.getSimpleName();
		}
		String methodName = null;
		if (method.isAnnotationPresent(TinyMethod.class)) {
			TinyMethod tinyMethod = method.getAnnotation(TinyMethod.class);
			methodName = tinyMethod.name();
		}
		if (methodName == null) {
			methodName = method.getName();
		}
		//
		String mapKey = MappingUtil.getMapKey(interfaceName, TinysqlSchema.SCRIPT.getName(), methodName);
		Script script = (Script)sqlMap.get(mapKey);
		//get args
		Map<String, Object> argMap = getArgMap(interfaceType, method, args);
		//get sql script
		SqlScript sqlScriptTemp = getSqlScript(interfaceType, method, args, script, argMap);
		//replace placeholder
		SqlScript sqlScript = sqlPlaceholderHandler(sqlScriptTemp, argMap);
		//
		Assert.notNull(sqlScript.getOriginSql(), "[Tinysql] missing sql script by key: " + mapKey + " , or other cause.");
		log.debug("[Tinysql] execute sqlScript.originSql is : \n {}", sqlScript.getOriginSql());
		log.debug("[Tinysql] execute sqlScript.paginationSql is : \n {}", sqlScript.getPaginationSql());
		log.debug("[Tinysql] execute sqlScript.countSql is : \n {}", sqlScript.getCountSql());
		log.debug("[Tinysql] execute argMap is : {}", argMap);
		//
		Class<?> returnTypeClazz = getReturnTypeClass(interfaceType, method);
		//
		if (ClazzUtil.isVoid(returnTypeClazz)) {
			throw new SqlParseException("[Tinysql] not supported return type, it can not be void.");
		}
		//init jdbc template
		initJdbcTemplate();		
		// ------------------------------batch insert update delete-------------------------------
		if (method.isAnnotationPresent(TinyBatch.class)) {
			TinyBatch tinyBatch = method.getAnnotation(TinyBatch.class);
			
			BatchSqlUpdate bsu = new BatchSqlUpdate();
			bsu.setJdbcTemplate(namedParameterJdbcTemplate.getJdbcTemplate());
			bsu.setSql(sqlScript.getOriginSql());
			bsu.setBatchSize(tinyBatch.batchSize());
			
			List<Map<String, Object>> argMapList = ArgumentUtil.processListArguments(method, args, sqlScript.getBatchParams());
			for(int ii=0;ii<argMapList.size();ii++) {
				Map<String, Object> argM = argMapList.get(ii);
				if(ii==0) {
					for(Map.Entry<String, Object> entry : argM.entrySet()){
						SqlTypeConverter stc = null;
						try {
							DatabaseMetaData md = namedParameterJdbcTemplate.getJdbcTemplate().getDataSource().getConnection().getMetaData();
							stc = new SqlTypeFactory().getSqlTypeConverter(md.getDatabaseProductName());
						} catch (SQLException e) {
							e.printStackTrace();
						}
						SqlParameter sp = new SqlParameter(entry.getKey(), stc.getJdbcTypeByObject(entry.getValue()));
						bsu.declareParameter(sp);
					}
				}
				int i = bsu.updateByNamedParam(argM);
				log.debug("[Tinysql] current batch affected rows: {}", i);
			}			
			bsu.flush();
			
			int retResult = argMapList==null?0:argMapList.size();
			log.debug("[Tinysql] total affected rows: {}", retResult);
			return retResult;
		}
		// ------------------------------call procedure or function-------------------------------
		else if (
				method.isAnnotationPresent(TinyCall.class)
				|| method.isAnnotationPresent(TinyFunc.class)
		) {
			SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(namedParameterJdbcTemplate.getJdbcTemplate());
			if(simpleJdbcCall.isFunction()) {//call function
				simpleJdbcCall.withFunctionName(sqlScript.getOriginSql());
			}else {//call procedure
				simpleJdbcCall.withProcedureName(sqlScript.getOriginSql());
			}
			
			Map<String, Object> retMap = null;
			
			if(sqlScript.getSqlParamSource()!=null) {
				retMap = simpleJdbcCall.execute(sqlScript.getSqlParamSource());
			}else {
				retMap = simpleJdbcCall.execute();
			}
			
			if(ClazzUtil.isCustomObject(returnTypeClazz)) {
				return JSONObject.parseObject(JSONObject.toJSONString(retMap), returnTypeClazz);
			}else if(ClazzUtil.isMap(returnTypeClazz)){
				return retMap;
			}
			return null;
		}
		// ------------------------------select-------------------------------
		else if (
				method.isAnnotationPresent(TinySelect.class) 
				|| StringUtil.startsWithAnyAndIgnoreCase(sqlScript.getOriginSql(), TinysqlConstants.queryPrefix)
		) {// select,with...select

			//generate redis cache key
			Map<String, Object> tempArgMap = (argMap==null)?(new HashMap()):argMap;
			final String redisCacheKey = StringUtil.join(secondCacheRedisPath, String.valueOf(sqlScript.getOriginSql().hashCode()), "-", String.valueOf(tempArgMap.hashCode()));
			//whether has redis second cache
			boolean hasSecondCache = false;

			if(secondCacheEnable) {//if second cache is enabled
				if (method.isAnnotationPresent(TinySecondCache.class) || sqlScript.isSecondCache()) {
					//init redis template
					initRedisTemplate();
					//whether exists redis cache key
					hasSecondCache = redisTemplate.hasKey(redisCacheKey);
					//get expire seconds
					if(sqlScript.isSecondCache()) {
						if(sqlScript.getCacheSeconds()<=0) {
							log.warn("[Tinysql] TinySecondCache value is let than 0, now change to global/default value {}", secondCacheGlobalExprie);
						}else {
							secondCacheGlobalExprie = sqlScript.getCacheSeconds();
						}
					}else {
						TinySecondCache tinySecondCache = method.getAnnotation(TinySecondCache.class);
						if(tinySecondCache.value()<=0) {
							log.warn("[Tinysql] TinySecondCache value is let than 0, now change to global/default value {}", secondCacheGlobalExprie);
						}else {
							secondCacheGlobalExprie = tinySecondCache.value();
						}
					}
				}
			}
			
			// ----------select for list
			if (ClazzUtil.isList(returnTypeClazz)) {//返回list
				if(hasSecondCache) {//if has second cache
					String cacheStr = redisTemplate.opsForValue().get(redisCacheKey);
					if(StringUtil.isNotEmpty(cacheStr)) {
						redisTemplate.expire(redisCacheKey, secondCacheGlobalExprie, TimeUnit.SECONDS);
						log.debug("[Tinysql] get resultset from second cache, redisCacheKey:{}", redisCacheKey);
						if(ClazzUtil.isResultColumnArrayList(returnTypeClazz)) {
							ResultColumnArrayList retList = new ResultColumnArrayList();
							JSONObject resultObj = JSON.parseObject(cacheStr);
							retList.addAll((List)resultObj.get(ResultColumnArrayList.Clazz.result));
							retList.setColumns((List)resultObj.get(ResultColumnArrayList.Fields.columns));
							retList.setProperties((List)resultObj.get(ResultColumnArrayList.Fields.properties));
							retList.setTotal((int)resultObj.get(ResultColumnArrayList.Fields.total));
							return retList;
						}else {
							return JSON.parseArray(cacheStr, getListGenericReturnType(interfaceType, method));
						}
					}
				}
				
				String queryListSql = sqlScript.getOriginSql();
				//super pagination
				if(sqlScript.isSuperPagination()) {
					queryListSql = sqlScript.getPaginationSql();
				}
				
				return namedParameterJdbcTemplate.query(queryListSql, argMap, new ResultSetExtractor() {
						@Override
						public Object extractData(ResultSet rs) throws SQLException, DataAccessException {
							//get return list's GenericType
							Class<?> genericType = getListGenericReturnType(interfaceType, method);
							Assert.notNull(genericType, "[Tinysql] error to get a null of generic return type ");
							
							Object retObj = null;
							if(ClazzUtil.isMap(genericType)) {
								retObj = ResultSetUtil.rs2List(rs);
							}else {
								retObj = ResultSetUtil.getReturnList(rs, genericType);
							}
							
							//wether return ResultColumnArrayList type
							if(ClazzUtil.isResultColumnArrayList(returnTypeClazz)) {
								ResultColumnArrayList retList = new ResultColumnArrayList();
								if(retObj!=null) {
									retList.addAll((ArrayList)retObj);
								}
								
								List<String> resultColumns = ResultSetUtil.getResultColumns(rs);
								List<String> resultProperties = ResultSetUtil.getResultProperties(rs);
								
								Method setResultColumnsMethod = null;
								Method setResultPropertiesMethod = null;
								try {
									setResultColumnsMethod = 
											ResultColumnArrayList.class.getMethod(ResultColumnArrayList.Methods.setColumns, List.class);
									setResultPropertiesMethod = 
											ResultColumnArrayList.class.getMethod(ResultColumnArrayList.Methods.setProperties, List.class);
								} catch (NoSuchMethodException e) {
									e.printStackTrace();
								} catch (SecurityException e) {
									e.printStackTrace();
								}
								if(setResultColumnsMethod!=null) {
									ReflectionUtil.invokeMethod(setResultColumnsMethod, retList, resultColumns);
								}
								if(setResultPropertiesMethod!=null) {
									ReflectionUtil.invokeMethod(setResultPropertiesMethod, retList, resultProperties);
								}
								
								//super pagination
								if(sqlScript.isSuperPagination()) {
									int rsTotal = namedParameterJdbcTemplate.queryForObject(sqlScript.getCountSql(), argMap, Integer.class);
									retList.setTotal(rsTotal);
								}
								
								//if need second cache, cache it
								if(secondCacheEnable && (method.isAnnotationPresent(TinySecondCache.class)||sqlScript.isSecondCache())) {
									log.debug("[Tinysql] set resultset to second cache, redisCacheKey:{}", redisCacheKey);
									redisTemplate.opsForValue().set(redisCacheKey, retList.toString());
									redisTemplate.expire(redisCacheKey, secondCacheGlobalExprie, TimeUnit.SECONDS);
								}
								
								return retList;
							}

							//if need second cache, cache it
							if(secondCacheEnable && (method.isAnnotationPresent(TinySecondCache.class)||sqlScript.isSecondCache())) {
								log.debug("[Tinysql] set resultset to second cache, redisCacheKey:{}", redisCacheKey);
								redisTemplate.opsForValue().set(redisCacheKey, JSON.toJSONString(retObj));
								redisTemplate.expire(redisCacheKey, secondCacheGlobalExprie, TimeUnit.SECONDS);
							}
							
							return retObj;
						}
				});
			}
			// ----------select for map或自定义对象
			else if (ClazzUtil.isCustomObject(returnTypeClazz) || ClazzUtil.isMap(returnTypeClazz)) {
				if(hasSecondCache) {//if has second cache
					String cacheStr = (String)redisTemplate.opsForValue().get(redisCacheKey);
					if(StringUtil.isNotEmpty(cacheStr)) {
						redisTemplate.expire(redisCacheKey, secondCacheGlobalExprie, TimeUnit.SECONDS);
						log.debug("[Tinysql] get resultset from second cache, redisCacheKey:{}", redisCacheKey);
						return JSON.parseObject(cacheStr, returnTypeClazz);
					}
				}
				
				return namedParameterJdbcTemplate.query(sqlScript.getOriginSql(), argMap, new ResultSetExtractor() {
					public Object extractData(ResultSet rs) throws SQLException,DataAccessException  {
						Object resultObj = ResultSetUtil.getReturnObject(rs, returnTypeClazz);

						//if need second cache, cache it
						if(secondCacheEnable && (method.isAnnotationPresent(TinySecondCache.class)||sqlScript.isSecondCache())) {
							log.debug("[Tinysql] set resultset to second cache, redisCacheKey:{}", redisCacheKey);
							redisTemplate.opsForValue().set(redisCacheKey, JSON.toJSONString(resultObj));
							redisTemplate.expire(redisCacheKey, secondCacheGlobalExprie, TimeUnit.SECONDS);
						}
						
						return resultObj;
					}
				});
			}
			// ----------select for 基础数据类型或基础数据类型的封装类
			else if (ClazzUtil.isPrimitiveOrWrap(returnTypeClazz)) {
				if(hasSecondCache) {//if has second cache
					String cacheStr = (String)redisTemplate.opsForValue().get(redisCacheKey);
					if(StringUtil.isNotEmpty(cacheStr)) {
						redisTemplate.expire(redisCacheKey, secondCacheGlobalExprie, TimeUnit.SECONDS);
						log.debug("[Tinysql] get resultset from second cache, redisCacheKey:{}", redisCacheKey);
						return JSON.parseObject(cacheStr, returnTypeClazz);
					}
				}
				
				Object resultObj = namedParameterJdbcTemplate.queryForObject(sqlScript.getOriginSql(), argMap, returnTypeClazz);

				//if need second cache, cache it
				if(secondCacheEnable && (method.isAnnotationPresent(TinySecondCache.class)||sqlScript.isSecondCache())) {
					log.debug("[Tinysql] set resultset to second cache, redisCacheKey:{}", redisCacheKey);
					redisTemplate.opsForValue().set(redisCacheKey, JSON.toJSONString(resultObj));
					redisTemplate.expire(redisCacheKey, secondCacheGlobalExprie, TimeUnit.SECONDS);
				}
				
				return resultObj;
			}
			// ------------其他返回类型不支持
			else {
				throw new SqlParseException("[Tinysql] not supported return type,only support for List/ Map,custom object/ Primitive, WrapClass.");
			}
		}
		// ------------------------------update,insert,delete-------------------------------
		else if (
				method.isAnnotationPresent(TinyUpdate.class)
				|| method.isAnnotationPresent(TinyInsert.class)
				|| method.isAnnotationPresent(TinyDelete.class)
				|| StringUtil.startsWithAnyAndIgnoreCase(sqlScript.getOriginSql(), TinysqlConstants.updatePrefix)
		) {
			return namedParameterJdbcTemplate.update(sqlScript.getOriginSql(), argMap);
		}
		// ------------------not supported script---------------------------------------------
		else {
			throw new SqlParseException("[Tinysql] not supported script...");
		}
	}

	@Override
	public void afterPropertiesSet() throws FileException {
		log.debug("[Tinysql] start to initialze mapping files......");

		log.debug("[Tinysql] validate config");
		validateConfig();

		log.debug("[Tinysql] processMappingFiles");
		processMappingFiles();

		log.debug("[Tinysql] initialze mapping files successful......");
	}

}
